# Python 趣味编程：第 2 章
#    例題 2.3 球和墙壁的建模
#  (2) 用墙反弹
# --------------------------
# 程序名: 02-ball-4.py

from tkinter import *
from dataclasses import dataclass
import time

# 参数定义汇总在一处
DURATION = 0.001    # sleep 时间 = 绘制的间隔
X0 = 150            # 球的 X 初始值
Y0 = 150            # 球的 Y 初始值
D = 15              # 球的直径
VX0 = 2             # 球的移动量

@dataclass
class Ball:
    id: int
    x: int
    y: int
    d: int
    vx: int
    c: str

@dataclass
class Border:
    left: int
    right: int
    top: int
    bottom: int

# 直径 d 若省略则为 3，颜色 c 若省略则为 "black"
# 速度vx省略后为2
def make_ball(x, y, d=3, vx=2, c="black"):
    id = canvas.create_rectangle(x, y, x + d, y + d,
                                 fill=c, outline=c)
    return Ball(id, x, y, d, vx, c)

# 从程序主体中抽出球的移动
def move_ball(ball):
    ball.x = ball.x + ball.vx

# 定义绘制墙壁的函数
def make_walls(ox, oy, width, height):
    canvas.create_rectangle(ox, oy, ox + width, oy + height)

# 一个重画球的函数，封装了 coords
def redraw_ball(ball):
    d = ball.d
    canvas.coords(ball.id, ball.x, ball.y,
                  ball.x + d, ball.y + d)

tk = Tk()
canvas = Canvas(tk, width=800, height=600, bd=0 )
canvas.pack()
tk.update()

# 给出墙壁的坐标(left, right, top, bottom)
border = Border(100, 700, 100, 500)

# 初始化处理
make_walls(
    border.left,
    border.top,
    border.right - border.left,
    border.bottom - border.top
    )
ball = make_ball(X0, Y0, D, VX0)

while True:
    move_ball(ball)   # 首先，使球移动
    # 如果移动后球的左上坐标比左墙壁更靠左，
    # 或者球的右端比右墙壁更靠右
    if (ball.x + ball.vx < border.left \
        or ball.x + ball.d  >= border.right):
        ball.vx = - ball.vx    # 反转球的移动方向
    redraw_ball(ball)      # 调用封装的函数，移动
    tk.update()            # 将绘图反映到画面上
    time.sleep(DURATION)
